/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.acme.server;

import com.google.appengine.api.datastore.DatastoreFailureException;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;


/**
 *
 * @author iiping
 */
public class DAOCentral {
  private DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
  
  public static final String KIND_TBL1 = "Tbl1KVP";
  public static final String KIND_TBL2 = "Tbl2KVP";
  public static final String KIND_SVR  = "AuthServers";


  private final Object  tbl1lock = new Object();
  private final Object  tbl2lock = new Object();
  private final Object  svrlock   = new Object();

  private static DAOCentral instance = new DAOCentral();

  private static final int expireCacheBySeconds = 60 * 5; // every 5 mins default
  private static final int expireCacheByDays = 60 * 60 * 24 * 10; // in 10 days




  public static DAOCentral getInstance(){
    return instance;
  }





  private DAOCentral(){
    
  }

  public void create(TOTable1 p){
    synchronized(tbl1lock){


      Entity n = new Entity(KIND_TBL1, p.getKey().toString());
      n.setProperty("value", p.getValue());
      try{
        datastore.put(n);
      }
      catch(IllegalArgumentException ex){
        //@TODO:handle
      }
      catch(ConcurrentModificationException ex){
        //@TODO:handle
      }
      catch(DatastoreFailureException ex){
        //@TODO:handle
      }
    }
  }

  private TOTable1 getToTbl1(Integer key)
  {
    synchronized(tbl1lock){
      TOTable1 retVal = null;

      if(key == null)
        return null;

      MemcacheService cache = MemcacheServiceFactory.getMemcacheService("nscache"+KIND_TBL1);

      TOTable1 searchableEntity = (TOTable1) cache.get(key);
      
      if(searchableEntity == null){ /* fetch from the datastore */


            Key keyToFind = KeyFactory.createKey(KIND_TBL1, key.toString());
            try {

              Entity e = datastore.get(keyToFind);

              retVal = new TOTable1();
              retVal.setKey(key);
              retVal.setValue((String)e.getProperty("value"));

              //then we cache
              cache.put(key, retVal,Expiration.byDeltaSeconds(expireCacheBySeconds));

            }
            catch(EntityNotFoundException ex){

            }
            catch(IllegalArgumentException ex){
              //@TODO:handle
            }
            catch(DatastoreFailureException ex){
              //@TODO:handle
            }
      } else {
        
        retVal = searchableEntity;
      }

      return retVal;
    }
  }

  public HashSet<TOTable1> retTOTable1(Integer keys[]){
    HashSet<TOTable1> retVal = new HashSet<TOTable1>();

    for(int i=0; keys.length > i ; i++){
      TOTable1 t = getToTbl1(keys[i]);
      if(t!=null)
        retVal.add(t);
    }
    return retVal;
  }

  public TOTable1 retTOTable1(Integer key){
    return getToTbl1(key);
  }


  public void create(TOTable2 p){
    synchronized(tbl2lock){

      Entity n = new Entity(KIND_TBL2, p.getKey().toString());
      n.setProperty("value", p.getValue());

      try{
        datastore.put(n);
      }
      catch(IllegalArgumentException ex){

      }
      catch(ConcurrentModificationException ex){

      }
      catch(DatastoreFailureException ex){

      }
    }
  }

  private TOTable2 getToTbl2(Integer key)
  {
    synchronized(tbl2lock){
      TOTable2 retVal = null;

      if(null == key)
        return null;

      MemcacheService cache = MemcacheServiceFactory.getMemcacheService("nscache"+KIND_TBL2);

      TOTable2 searchableEntity = (TOTable2) cache.get(key);

      if(searchableEntity == null){ /* fetch from the datastore */


                Key keyToFind = KeyFactory.createKey(KIND_TBL2, key.toString());
                try {
                  Entity e = datastore.get(keyToFind);

                  retVal = new TOTable2();
                  retVal.setKey(key);
                  retVal.setValue(new Integer(((Long)e.getProperty("value")).intValue()));

                  //now cache it
                  cache.put(key, retVal, Expiration.byDeltaSeconds(expireCacheBySeconds));

                }
                catch(EntityNotFoundException ex){

                }
                catch(IllegalArgumentException ex){
                  //@TODO:handle
                }
                catch(DatastoreFailureException ex){
                  //@TODO:handle
                }
    } else{
        retVal = searchableEntity;
    }

      return retVal;
    }
  }

  public HashSet<TOTable2> retTOTable2(Integer keys[]){
    HashSet<TOTable2> retVal = new HashSet<TOTable2>();

    for(int i=0; keys.length > i ; i++){
      TOTable2 t = getToTbl2(keys[i]);
      if(t!=null)
        retVal.add(t);
    }
    return retVal;
  }

  public TOTable2 retTOTable2(Integer key){
    return getToTbl2(key);
  }



  public void updateTOTbl2Key(TOTable2 entity){
    synchronized(tbl2lock){

      //prio cache
      cleanTbl2Cache(entity.getKey());


            Key keyToFind = KeyFactory.createKey(KIND_TBL2, entity.getKey().toString());
            try {
              Entity e = datastore.get(keyToFind);
              e.setProperty("value", entity.getValue());
              datastore.put(e);

            } catch (EntityNotFoundException ex) {

            }

    }

  }

  public synchronized void deleteTOTbl2Key(Integer key){
    synchronized(tbl2lock){
      cleanTbl2Cache(key);
      

          Key keyToFind = KeyFactory.createKey(KIND_TBL2, key.toString());
          datastore.delete(keyToFind);
    }

  }

  private void cleanTbl2Cache(Integer key){
      //prio cache
      MemcacheService cache = MemcacheServiceFactory.getMemcacheService("nscache"+KIND_TBL2);
      Object searchableEntity =  cache.get(key);
      if(null != searchableEntity)
        cache.delete(searchableEntity);
  }
  
  
  
  

  public boolean isServerAuthenticated(TOServer server){
	  if(null == server || server.getIPAddress() == null)
		  return false;
	  Key keyToSearch = KeyFactory.createKey(KIND_SVR, server.getIPAddress());
	  TOServer regSvr = getTOServer(keyToSearch);
	  if(regSvr == null)
		  return false;
	  return regSvr.equals(server);
  }

  public HashSet<TOServer> getServers(){
    synchronized(svrlock){
    	HashSet<TOServer> retVal = new HashSet<TOServer>();
    	Query qServers = new Query(KIND_SVR);
    	qServers.setKeysOnly();
    	
    	List<Entity> servers = datastore.prepare(qServers).asList(FetchOptions.Builder.withDefaults());
    	
    	for(Entity e: servers){
    		TOServer reslt = getTOServer(e.getKey());
    		if(null != reslt){
    			retVal.add(reslt);
    			
    		}
    	}
    	
    	return retVal;
    }
  }
  
  private TOServer getTOServer(Key serverKey)
  {
    synchronized(tbl2lock){
      TOServer retVal = null;

      if(null == serverKey)
        return null;
      
      MemcacheService cache = MemcacheServiceFactory.getMemcacheService("nscache"+KIND_SVR);
      TOServer searchableEntity = (TOServer) cache.get(KeyFactory.keyToString(serverKey));

      if(searchableEntity == null){ /* fetch from the datastore */
    	  
                try {
                  Entity e = datastore.get(serverKey);
                  retVal = new TOServer(serverKey.getName());
                  retVal.setAuthCodeNoEnc(((String)(e.getProperty("authcode"))));
                  
                  //now cache it
                  cache.put(retVal.getIPAddress(), retVal, Expiration.byDeltaSeconds(expireCacheByDays));
                  
                }
                catch(EntityNotFoundException ex){

                }
                catch(IllegalArgumentException ex){
                  //@TODO:handle
                }
                catch(DatastoreFailureException ex){
                  //@TODO:handle
                }
    } else{
        retVal = searchableEntity;
    }

      return retVal;
    }
  }  

  public void addAuthServer(TOServer server){
    synchronized(svrlock){
    	
    	Key key = KeyFactory.createKey(KIND_SVR, server.getIPAddress());
        Entity n = new Entity(key);
        n.setProperty("authcode", server.getAuthCode());

        try{
          datastore.put(n);
        }
        catch(IllegalArgumentException ex){
        	//@TODO
        }
        catch(ConcurrentModificationException ex){
        	//@TODO
        }
        catch(DatastoreFailureException ex){
        	//@TODO
        }
    }
  }
  
  public void deleteAuthServer(TOServer server){
    synchronized(svrlock){
    	cleanTblSrv(server.getIPAddress());
        Key keyToFind = KeyFactory.createKey(KIND_SVR, server.getIPAddress());
        datastore.delete(keyToFind);
    }
  }
  
  public void updateAuthServer(TOServer orig, TOServer n){
	  synchronized(svrlock){
		  cleanTblSrv(orig.getIPAddress());
          Key keyToFind = KeyFactory.createKey(KIND_SVR, orig.getIPAddress());
          try {
            Entity e = datastore.get(keyToFind);
            e.setProperty("authcode", n.getAuthCode());
            datastore.put(e);

          } catch (EntityNotFoundException ex) {

          }
          
	  }
  }

  
  private void cleanTblSrv(String serverIP){
      
      MemcacheService cache = MemcacheServiceFactory.getMemcacheService("nscache"+KIND_SVR);
      TOServer searchableEntity =  (TOServer)cache.get(serverIP);
      if(null != searchableEntity){
        cache.delete(searchableEntity);
        
        
      }
  }


}
